﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.BusinessData.Infrastructure;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.SharePoint.BusinessData.Administration;
using Microsoft.SharePoint.BusinessData.Infrastructure;

namespace Office365.Helper.BCSModeler
{
    public class Wcf
    {
        #region Privates
        private string _siteUrl;
        private string _serviceUrl;
        private XDocument _wsdlDefinition;
        private XDocument _wsdlNamespace;
        private XDocument _wsdlSchema;
        #endregion

        #region cstors
        /// <summary>
        /// Access modifier set to 'private' to force the use of the constructor with parameters.
        /// </summary>
        private Wcf()
        {
        }

        public Wcf(string siteUrl, string serviceUrl)
        {
            _siteUrl = siteUrl;
            _serviceUrl = serviceUrl;
            LoadWsdls();
        }

        public void Generate()
        {
            BdcController controller = new BdcController(_siteUrl);

            // Create Model
            Model model = controller.CreateModel("Client");
            
            // Make a new LobSystem
            LobSystem lobSystem = controller.CreateLobSystem(ref model, _serviceUrl + "?wsdl", Microsoft.BusinessData.MetadataModel.SystemType.Wcf);
            
            // Set the system properties
            controller.AddProperty(lobSystem, "ReferenceKnownTypes", true);
            controller.AddProperty(lobSystem, "WcfMexDiscoMode", "Disco");
            controller.AddProperty(lobSystem, "WcfMexDocumentUrl", _serviceUrl + "?wsdl");
            controller.AddProperty(lobSystem, "WcfProxyNamespace", "BCSServiceProxy");
            controller.AddProperty(lobSystem, "WildcardCharacter", "*");
            controller.AddProperty(lobSystem, "WsdlFetchAuthenticationMode", "Credentials");
            controller.AddProperty(lobSystem, "WsdlFetchSsoApplicationId", "WCFServiceContent");
            controller.AddProperty(lobSystem, "WsdlFetchSsoProviderImplementation", "Microsoft.Office.SecureStoreService.Server.SecureStoreProvider, Microsoft.Office.SecureStoreService, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");

            // Generate service proxy
            controller.GenerateProxy(ref lobSystem);

            // Make a new LobSystemInstance
            LobSystemInstance lobSystemInstance = controller.CreateLobSystemInstance(lobSystem, _serviceUrl + "?wsdl");

            // Set the connection properties
            controller.AddProperty(lobSystemInstance, "ShowInSearchUI", string.Empty);
            controller.AddProperty(lobSystemInstance, "SsoApplicationId", "WCFServiceContent");
            controller.AddProperty(lobSystemInstance, "SsoProviderImplementation", "Microsoft.Office.SecureStoreService.Server.SecureStoreProvider, Microsoft.Office.SecureStoreService, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
            controller.AddProperty(lobSystemInstance, "WcfAuthenticationMode", "Credentials");
            controller.AddProperty(lobSystemInstance, "WcfEndpointAddress", _serviceUrl);

            // Create new entity
            Entity entity = controller.AddEntity("Client", "http://winsrv/", "1.0.0.0", 10000, Microsoft.BusinessData.Runtime.CacheUsage.Default, lobSystem, model, false);
            controller.CreateIdentifier(ref entity, "ClientId", typeof(int), false);

            Method method = controller.CreateMethod(entity, "CreateClient", false, false, "Client");

            controller.AddAccessControlEntry(ref entity, @"WINSRV\joshua", BdcRights.Edit | BdcRights.Execute | BdcRights.SetPermissions | BdcRights.SelectableInClients, true);
            
            Parameter param = controller.AddParameters(ref method, "CreateClientContent", true, DirectionType.In);
            TypeDescriptor typeDes = controller.AddTypeDescriptor(ref param, "CreateClientContent", true, "BCSServiceProxy.ClientContent, Client", "CreateClientContent", null, null, TypeDescriptorFlags.None, null);

            controller.AddTypeDescriptor(ref typeDes, "ClientId", true, typeof(int), "ClientId", controller.GetIdentifierReference("ClientId", entity), null, TypeDescriptorFlags.CreatorField, null);
            controller.AddTypeDescriptor(ref typeDes, "ClientName", true, typeof(string), "ClientName", null, null, TypeDescriptorFlags.CreatorField, null);
            controller.AddTypeDescriptor(ref typeDes, "CreatedBy", true, typeof(string), "CreatedBy", null, null, TypeDescriptorFlags.CreatorField, null);
            controller.AddTypeDescriptor(ref typeDes, "CreatedOn", true, typeof(DateTime), "CreatedOn", null, null, TypeDescriptorFlags.CreatorField, null);
            controller.AddTypeDescriptor(ref typeDes, "ModifiedBy", true, typeof(string), "ModifiedBy", null, null, TypeDescriptorFlags.CreatorField, null);
            controller.AddTypeDescriptor(ref typeDes, "ModifiedOn", true, typeof(DateTime), "ModifiedOn", null, null, TypeDescriptorFlags.CreatorField, null);
            controller.AddTypeDescriptor(ref typeDes, "StartDate", true, typeof(DateTime), "StartDate", null, null, TypeDescriptorFlags.CreatorField, null);

            // TODO: Add Association Reference
            //AssociationReference refer = new AssociationReference("GetClientsForStatus", entity, catalog);
            //controller.AddTypeDescriptor(ref typeDes, "StatusId", true, typeof(int), "CreatedOn", controller.GetIdentifierReference("StatusId", entity), null, TypeDescriptorFlags.CreatorField, refer);
            
            /* TODO: Add Interpretation Rules
            InterpretedRule rule = new DateTimeNormalize(DateTimeKind.Utc);
            td.Interpretation.Add(rule);
            */

            controller.ActivateEntity(entity);


            System.IO.File.WriteAllText(@"C:\model.txt", model.WriteXml(Microsoft.SharePoint.BusinessData.Parser.PackageContents.All));
            //System.IO.File.WriteAllText(@"C:\lobsystem.txt", lobSystem.WriteXml(Microsoft.SharePoint.BusinessData.Parser.PackageContents.All));
            //System.IO.File.WriteAllText(@"C:\lobinstance.txt", lobSystemInstance.WriteXml(Microsoft.SharePoint.BusinessData.Parser.PackageContents.All));
            //System.IO.File.WriteAllText(@"C:\entity.txt", entity.WriteXml(Microsoft.SharePoint.BusinessData.Parser.PackageContents.All));
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Load WSDL definition and types
        /// </summary>
        private void LoadWsdls()
        {
            WebClient client = new WebClient();
            
            // Download the WSDL and parse as XML
            _wsdlDefinition = XDocument.Parse(client.DownloadString(_serviceUrl + "?wsdl"));
            _wsdlNamespace = XDocument.Parse(client.DownloadString(_serviceUrl + "?xsd=xsd0"));
            _wsdlSchema = XDocument.Parse(client.DownloadString(_serviceUrl + "?xsd=xsd2"));
        }

        #endregion
    }
}
